#include "lexer.h"

Token::Token(TokenType type, std::string value) : type(type), value(value) {}

std::string Token::to_string()
{
	std::string ret;
	switch (type)
	{
	case ELEMENT:
		ret = "Element";
		break;
	case NUMBER:
		ret = "Number";
		break;
	case SPACE:
		ret = "Space";
		break;
	case LEFTPARENTH:
		ret = "LeftParenth";
		break;
	case RIGHTPARENTH:
		ret = "RightParenth";
		break;
	case ENDOFFILE:
		ret = "EndOfFile";
		break;
	}
	ret += '{';
	ret += value;
	ret += '}';
	return ret;
}

Lexer::Lexer() = default;
Lexer::Lexer(std::string input) {
	size_t first = input.find_first_not_of(' ');
	if (std::string::npos == first) {
		this->input = input;
	} else {
		size_t last = input.find_last_not_of(' ');
		this->input = input.substr(first, (last - first + 1));
	}
}

std::vector<Token> Lexer::tokenize()
{
	scanToken();
	std::vector<Token> filtered_tokens;
	filtered_tokens.push_back(tokens[0]);
	for (int i = 1; i < tokens.size() - 1; ++i) {
		if (tokens[i].type == TokenType::SPACE 
			&& (tokens[i - 1].type == TokenType::LEFTPARENTH 
			|| tokens[i + 1].type == TokenType::RIGHTPARENTH)) {
		} else {
			filtered_tokens.push_back(tokens[i]);
		}
	}
	return filtered_tokens;
}

bool Lexer::isEnd() {
	return position >= input.length();
}

char Lexer::advance() {
	if (isEnd()) {
		++position;
		return '\0';
	}
	return input[position++];
}

void Lexer::backward() {
	if (position > 0) {
		--position;
	}
}

char Lexer::peek() {
	if (isEnd()) {
		return '\0';
	}
	return input[position];
}

void Lexer::scanToken() {
	while (!isEnd()) {
		char c = advance();
		switch (c) {
		case '(':case '[':case '{':
			tokens.push_back(Token(TokenType::LEFTPARENTH, std::string("(")));
			break;

		case ')':case ']': case '}':
			tokens.push_back(Token(TokenType::RIGHTPARENTH, std::string(")")));
			break;

		case ' ':
			tokens.push_back(Token(TokenType::SPACE, std::string(" ")));
			while (advance() == ' ');
			backward();
			break;

		case '0': case '1': case '2':
		case '3': case '4': case '5':
		case '6': case '7': case '8':
		case '9': case '.':
			backward();
			addNumber();
			break;

		default:
			if (c >= 'A' && c <= 'Z') {
				backward();
				addElement();
			}
			else {
				error(std::string("No such type in input"));
			}
			break;
		}
	}
	tokens.push_back(Token(TokenType::ENDOFFILE, std::string("\0")));
}

void Lexer::error(std::string info) {
	throw std::exception((info + " : position at " + std::to_string(position)).c_str());
}

void Lexer::addNumber() {
	std::string number;
	char c = advance();
	for (; (c >= '0' && c <= '9' || c == '.'); ) {
		number.push_back(c);
		c = advance();
	}
	backward();
	tokens.push_back(Token(TokenType::NUMBER, number));
}

void Lexer::addElement() {
	std::string element;
	element.push_back(advance());
	if (peek() >= 'a' && peek() <= 'z') {
		element.push_back(advance());
	}
	tokens.push_back(Token(TokenType::ELEMENT, element));
}